local map_mgr = _G.map_mgr
local lua_aoi = _G.lua_aoi
local ExcelMonster = require("excel.Monster")
local Map = require("core.Map")
local MapEx = require("core.MapEx")
local Msg = require("core.Msg")
local Obj = require("core.Obj")
local ObjMonster = require("core.ObjMonster")
local SceneEvent = require("core.SceneEvent")
local RoleDefine = require("role.RoleDefine")
local Timer = require("core.Timer")
local ObjHuman = require("core.ObjHuman")
local SkillConditions = require("skill.Conditions")
local Skill = require("skill.Skill")
local CoolDown = require("skill.CoolDown")
local ExcelSkill = require("excel.Skill")
local Buffer = require("buffer.Buffer")
local EffectLogic = require("scene.EffectLogic")
local ObjCollect = require("core.ObjCollect")
local ObjItem = require("core.ObjItem")
local Status = require("scene.Status")
local BufferCmds = require("buffer.Cmds")

data = {}
condition = {}  --条件
action = {}     --动作
local action_cmd = {"action_cmd1", "action_cmd2", "action_cmd3", "action_cmd4", "action_cmd5"}
local action_arg = {"action_arg1", "action_arg2", "action_arg3", "action_arg4", "action_arg5"}

local list_all = {}
local list_all_len
local list_enemy = {}
local list_enemy_len
local list_friend = {}
local list_friend_len

local condition_check = {}
local action_set = {}
local group_hash = {}
local group_monster_has_attacker_id = {}

local monster_id_to_ai = {
  be_attacked = {}, 
  has_target = {}, 
  no_target = {}, 
  on_die = {}, 
  target_change = {},
}

for k, v in pairs(MapEx) do
    if type(k) == "number" and v.monster then
        for kk, vv in pairs(v.monster) do
            if 0 < kk then
                for _, vvv in ipairs(vv) do
                    group_hash[vvv[1]] = group_hash[vvv[1]] or {}
                    group_hash[vvv[1]][vvv[2]] = group_hash[vvv[1]][vvv[2]] or {}
                    group_hash[vvv[1]][vvv[2]][vvv[3]] = kk
                end
            end
        end
    end
end


local tb_ai_id = {}
function get_ai_by_bag(out, ai_bag, callback)
    local len = 0
    for i = 1, ai_bag[0] or #ai_bag do
        for _, ai_id in ipairs(ExcelMonster.ai_bag[ai_bag[i]].tb_ai) do
            local t
            if type(ai_id) == "number" then
                t = tb_ai_id
                t[1] = ai_id
                t[2] = ai_id
            else
                t = ai_id
            end
            for j = t[1], t[2] do
                local ai_id = j
                local ai = ExcelMonster.ai[ai_id]
                if not ai then
                    assert(nil, ai_id)
                end
                local t = ExcelMonster.ai[ai_id].callback
                if t == callback or t == "has_or_no_target" and (callback == "has_target" or callback == "no_target") then
                    len = len + 1
                    out[len] = ai_id
                end
            end
        end
    end
    return len
end

for k, v in pairs(monster_id_to_ai) do
  for kk, vv in pairs(ExcelMonster.monster) do
        v[kk] = {}
        get_ai_by_bag(v[kk], vv.ai_need, k)
  end
end
local function get_group(monster)
    local t = group_hash[monster.monster_id]
    if t then
        t = t[monster.born_x]
        if t then
            return t[monster.born_y]
        end
    end
end

local function get_val(monster)
    if not monster.val then
        monster.val = {}
    end
    return monster.val
end

local function is_hide(obj)
    return obj.hide or obj.status == Status.HUMAN_STATUS_HIDE
end

function init_ai_data(monster)
    monster.attacker_id = nil
    monster.attacker_uid = nil
    monster.lock_target_time = nil
    monster.lock_target = nil
    monster.ai_event_cnt = {}
    monster.ai_event_time = {}
    monster.has_do_on_die = nil
    monster.wake_up_time = nil
    monster.walk_sleep = nil
    monster.last_ai_id = nil
    monster.skills = nil
    monster.use_skill_tb = nil
    monster.use_skill_pos = nil
    monster.val = {}
end
local function get_skills(monster)
    return monster.skills or ExcelMonster.monster[monster.monster_id].skills
end


local function get_val_by_type(monster, key)
    local monster_type = ExcelMonster.monster[monster.monster_id].monster_type
    return ExcelMonster.monster_type[monster_type][key]
end

local function is_in_area_id(scene_id, x, y, area_id)
    local map_id = Map.scene_id_to_map_id[scene_id]
    for _, v in ipairs(MapEx[map_id].rect[area_id]) do
        if v[1] < x and x < v[3] and v[2] < y and y < v[4] then
            return true
        end
    end
end

local function is_near_grid(a, b)
    local tile = 100
    local ddx = a.x - b.x
    local ddy = a.y - b.y
    if math.abs(ddx) > 110 or math.abs(ddy) >110 then
        return
    end
    local dx = math.floor(a.x / tile) - math.floor(b.x / tile)
    local dy = math.floor(a.y / tile) - math.floor(b.y / tile)
    return dx * dx + dy * dy < 3
end


function onOwnerAttack(sender, recv)
    if not recv or not SkillConditions.isEnemy(sender, recv) then
        return
    end
    if Obj.TYPE_HUMAN == sender.obj_type then
        sender.onOwnerAttack_recv_id = recv.id
    end
    if Obj.TYPE_HUMAN == recv.obj_type then
        recv.onOwnerAttack_recv_id = sender.id
    end
end



local callback_tb_cache = {}
function do_ai_by_callback(monster, attacker, callback)
    local len = 0
    local callback_tb = callback_tb_cache
    if monster.ai_bag then
        len = get_ai_by_bag(callback_tb, monster.ai_bag, callback)
    else
        callback_tb = monster_id_to_ai[callback][monster.monster_id]
        if not callback_tb then
            return
        end
        len = #callback_tb        
    end
    if len < 1 then
        return
    end
    if callback ~= "has_target" and callback ~= "no_target" then
        for i = 1, len do
            do_ai_by_id(monster, attacker, callback_tb[i])
        end
        return
    end

    local t = monster.last_ai_id
    if t == 0 then
        assert()
    end
    if not t or len < t then
        t = 0
        monster.last_ai_id = len
    end
    for _ = 1, 1111 do
        t = t + 1
        if len < t then
            t = 1
        end
        local ai_id = callback_tb[t]
        if do_ai_by_id(monster, attacker, ai_id, t) then
            monster.last_ai_id = t
            return
        end
        if t == monster.last_ai_id then
            return
        end
    end
    assert(nil, monster.monster_id)
end

function do_ai_by_id(monster, attacker, ai_id)
    local ai = ExcelMonster.ai[ai_id]
    if monster.ai_event_time[ai_id] and (0 < ai.cnt and ai.cnt <= monster.ai_event_cnt[ai_id] or Timer.now - monster.ai_event_time[ai_id]< ai.cd * 1000) then
        return
    end
    data.monster = monster
    data.attacker = attacker
    data.cur_ai = ai_id
    if  not((ai.condition_cmd1 == "" or condition[ai.condition_cmd1](ai.condition_arg1[1], ai.condition_arg1[2], ai.condition_arg1[3], ai.condition_arg1[4])) and
        (ai.condition_cmd2 == "" or condition[ai.condition_cmd2](ai.condition_arg2[1], ai.condition_arg2[2], ai.condition_arg2[3], ai.condition_arg2[4])) and
        (ai.condition_cmd3 == "" or condition[ai.condition_cmd3](ai.condition_arg3[1], ai.condition_arg3[2], ai.condition_arg3[3], ai.condition_arg3[4])) and
        (ai.condition_cmd4 == "" or condition[ai.condition_cmd4](ai.condition_arg4[1], ai.condition_arg4[2], ai.condition_arg4[3], ai.condition_arg4[4])) and
        (ai.condition_cmd5 == "" or condition[ai.condition_cmd5](ai.condition_arg5[1], ai.condition_arg5[2], ai.condition_arg5[3], ai.condition_arg5[4]))) then
        return
    end
    if needTalk then
        local mm = Msg.gc.GC_OBJ_TALK
        mm.obj_id = monster.id
        mm.txt = "aiId" .. ai_id
        Obj.sendArea(mm, monster.scene_id, monster.x, monster.y)
    end
    if monster.ai_event_cnt[ai_id] then
        monster.ai_event_cnt[ai_id] = monster.ai_event_cnt[ai_id] + 1
    else
        monster.ai_event_cnt[ai_id] = 1
    end
    monster.ai_event_time[ai_id] = Timer.now
    for k, v in ipairs(action_cmd) do
        local t = ai[v]
        if t == "" then
            break
        end
        local arg = ai[action_arg[k]]
        data.action_cmd = k
        if not action[t] then
            assert(nil, t)
        end
        action[t](arg[1], arg[2], arg[3], arg[4])
    end
    return true
end
function ai()
    for scene_id in pairs(Obj.scenes) do
        for i = 1, 128 do
            group_monster_has_attacker_id[i] = nil
        end

        list_all_len = 0
        list_enemy_len = 0
        list_friend_len = 0
        --设置9宫格
        lua_aoi.grid9_test_add()
        map_mgr.monster_clear()
        SceneEvent.setBan(scene_id)
        
        for _, v in pairs(Obj.scenes[scene_id]) do
            v.same_pos_monster = nil
            if Obj.getHp(v) > 0 then
                if v.obj_type == Obj.TYPE_HUMAN then
                    lua_aoi.grid9_set(v.x,v.y)
                end
                if v.monster_id then
                    if 0 < v.path_len and v.path then
                        v.same_pos_monster = map_mgr.monster_set(v.path[1], v.path[2])
                    else
                        v.same_pos_monster = map_mgr.monster_set(v.x, v.y)
                    end
                    if v.attacker_id then
                        local group = get_group(v)
                        if group then
                            group_monster_has_attacker_id[group] = v.attacker_id
                        end
                    end
                end
                if v.monster_id or v.db and not is_hide(v) then
                    list_all_len = list_all_len + 1
                    list_all[list_all_len] = v.id
                    if (v.obj_type == Obj.TYPE_HUMAN or v.monster_id) and v.camps == Obj.CAMPS_BAD then
                        list_enemy_len = list_enemy_len + 1
                        list_enemy[list_enemy_len] = v.id
                    elseif v.obj_type == Obj.TYPE_HUMAN or v.monster_id and v.camps == Obj.CAMPS_GOOD then
                        list_friend_len = list_friend_len + 1
                        list_friend[list_friend_len] = v.id
                    end
                end
            end
        end
        for _, v in pairs(Obj.scenes[scene_id]) do
            if v.monster_id and 0 < v.hp and not v.attacker_id then
                local group = get_group(v)
                if group then
                    local id = group_monster_has_attacker_id[group]
                    local t = Obj.objs[id]
                    if t then
                        local dx = v.x - (v.start_point_x or v.born_x)
                        local dy = v.y - (v.start_point_y or v.born_y)
                        local ddx = t.x - (v.start_point_x or v.born_x)
                        local ddy = t.y  - (v.start_point_y or v.born_y)
                        local r = ExcelMonster.monster[v.monster_id].radius_attack
                        r = r* r
                        if dx * dx + dy * dy < r and ddx*ddx + ddy*ddy < r then
                            v.attacker_id = id
                            v.attacker_uid = t.uid
                        end
                    end
                end
            end
        end        
        for _, v in pairs(Obj.scenes[scene_id]) do
            if not is_hide(v) and v.monster_id then
                monster_ai(v)
            end
        end
    end
end

function monster_ai(monster)
    local cf = ExcelMonster.monster[monster.monster_id]
    local attacker
    if monster.attacker_id then
        attacker =  Obj.getObj(monster.attacker_id, monster.attacker_uid)
    end
    if monster.hp <1 and not monster.has_do_on_die then
        monster.has_do_on_die = true
        do_ai_by_callback(monster, attacker, "on_die")
        return
    end

    if monster.dieTime and cf.die_remain * 1000 <= Timer.now - monster.dieTime then
        if monster.need_reborn then
            SceneEvent.add_reborn_time(monster)
        end
        ObjMonster.destroy(monster)
        return
    end
    if monster.hp < 1 then
        return
    end
    if monster.ref_ai_id then
        do_ai_by_id(monster, attacker, monster.ref_ai_id)
        monster.ref_ai_id = nil
    end

    if monster.wake_up_time and Timer.now < monster.wake_up_time then
        return
    end
    if monster.walk_sleep then
        if monster.path_len > 0 then
            return
        end
        monster.walk_sleep = nil
    end

    if monster.qianyao_begin_time then
        if monster.qianyao_begin_time < Timer.now then
            monster.qianyao_begin_time = nil
            if monster.status == Status.HUMAN_STATUS_ROTATION or monster.status == Status.HUMAN_STATUS_ROTATION1 or monster.status == Status.HUMAN_STATUS_GUIDE or monster.status == Status.HUMAN_STATUS_SING then
                return
            end
            Skill.use(monster, monster.qianyao_skill_id, monster.qianyao_x, monster.qianyao_y, monster.x, monster.y, attacker,monster.qianyao_skill_lv)
        end
        return
    end
    if monster.use_skill_pos then
        if attacker then
            skill(monster, monster.use_skill_tb[monster.use_skill_pos], attacker.x, attacker.y, attacker)
        end
        if monster.use_skill_pos < #monster.use_skill_tb then
            monster.use_skill_pos = monster.use_skill_pos + 1
        else
            monster.use_skill_pos = nil
            monster.use_skill_tb = nil
        end
        return
    end


    if monster.be_attacked_id then
        local t = Obj.objs[monster.be_attacked_id]
        if t then
            do_ai_by_callback(monster, t, "be_attacked")
            attacker = attacker or t
        end
        monster.be_attacked_id = nil
    end

    if monster.attacker_id then
        if attacker and attacker.scene_id == monster.scene_id and 0 < Obj.getHp(attacker) and not is_hide(attacker) then
            if monster.attacker_id_old ~= monster.attacker_id then
                monster.attacker_id_old = monster.attacker_id
                monster.lock_target_time = Timer.now
                do_ai_by_callback(monster, attacker, "target_change")
            end
            do_ai_by_callback(monster, attacker, "has_target")
            return
        end
        monster.attacker_id = nil
        monster.attacker_uid = nil
    end
    monster.attacker_id_old = nil
    monster.lock_target_time = nil
    do_ai_by_callback(monster, nil, "no_target")
end

function on_move(monster,x,y, direct)
    if not Status.canMove(monster) then --不能移动
        return
    end
    x = math.floor(x)
    y = math.floor(y)
    if 0 < monster.path_len and monster.path then
        if monster.tx == x and monster.ty == y then
            return
        end
    end

    monster.tx = x
    monster.ty = y

    monster.path = monster.path or {}
    local path_len = 0
    if not direct and get_val_by_type(monster, "is_fly") == 0 then
        local map_id = Map.scene_id_to_map_id[monster.scene_id]
        local attacker = Obj.getObj(monster.attacker_id, monster.attacker_uid)
        if attacker then
            local x = attacker.x
            local y = attacker.y
            if attacker.path_len and 0 < attacker.path_len then
                if attacker.path then
                    x = attacker.path[1]
                    y = attacker.path[2]
                end
            end
            local tile = 100
            if math.floor(monster.tx / tile) == math.floor(x / tile) and math.floor(monster.ty / tile) == math.floor(y / tile) then
                map_mgr.monster_set(x, y)
            end
        end
        path_len = map_mgr.get_dfs_path(map_id, monster.path, monster.x, monster.y, monster.tx, monster.ty)
    else
        monster.path[1] = monster.tx
        monster.path[2] = monster.ty
        path_len = 1
    end
    monster.path_len = path_len
    if 0 < path_len then
        map_mgr.monster_set(monster.path[1], monster.path[2])
        Obj.movePath(monster)
    end

end



local function set_attacker(s, t)
    if not s then
        return
    end
    if not t then
        return
    end
    if s == t then
        assert()
    end
    s.attacker_id = t.id
    s.attacker_uid = t.uid
end

function action.action(action_id, dir)
    if dir and 0<=dir and dir < 8 then
        data.monster.dir =dir
    end
    Obj.resetPathParam(data.monster)
    local mm = Msg.gc.GC_OBJ_ACTION
    mm.obj_id = data.monster.id
    mm.dir = dir and 0<=dir and dir < 8 and dir or 8
    mm.action_id = action_id
    Obj.sendArea(mm,data.monster.scene_id,data.monster.x,data.monster.y)
end

function action.lock_target()
    local monster = data.monster
    monster.lock_target = monster.uid
end

function action.unlock_target()
    local monster = data.monster
    monster.lock_target = nil
end

function action.lock_target_monster_id(monster_id)
    local monster = data.monster
    local camps = monster.camps or 0
    if camps == Obj.CAMPS_NONE or camps == Obj.CAMPS_BAD then
        local list = list_friend
        local list_len = list_friend_len
        for i = 1, list_len do
            local v = Obj.objs[list[i]]
            if v and v.monster_id == monster_id then
                set_attacker(monster, v)
                action.lock_target()
                return
            end
        end
    end
    if camps == Obj.CAMPS_NONE or camps == Obj.CAMPS_GOOD then
        local list = list_enemy
        local list_len = list_enemy_len
        for i = 1, list_len do
            local v = Obj.objs[list[i]]
            if v and v.monster_id == monster_id then
                set_attacker(monster, v)
                action.lock_target()
                return
            end
        end
    end
end

function action.add_buffer_self(buffer_id)
    Buffer.add(buffer_id, data.monster, data.monster)
end

function action.add_hp(per)
    ObjMonster.setHp(data.monster, ObjMonster.getHp(data.monster) + ObjMonster.getHpMax(data.monster) * per)
end

function action.call_collect(collect_id, cnt, r)
    local monster = data.monster
    local scene_id = monster.scene_id
    local cur = math.random() * math.pi * 2
    local per = math.pi * 2 / cnt
    for i = 1, cnt do
        local x = monster.x +  r * math.cos(cur) * 1.4
        local y = monster.y + r * math.sin(cur)
        cur = cur + per
        local mm = Msg.gc.GC_SHOW_DISPLAY
        mm.body_id = 1132
        mm.x = x
        mm.y = y
        mm.effectConfig = EffectLogic.getConfig(mm.body_id)
        Obj.sendArea(mm, scene_id, x, y)
        local sub = ObjCollect.create(collect_id)
        ObjCollect.enterScene(sub, scene_id, x, y)
    end
end

function action.call_monster(monster_id, cnt, r)
    local monster = data.monster
    local scene_id = monster.scene_id
    for i = 1, cnt do
        local sub = ObjMonster.create(monster_id)
        local hudu = math.random() * 2 * math.pi
        local x = monster.x + math.cos(hudu) * r
        local y = monster.y + math.sin(hudu) * r
        ObjMonster.enterScene(sub, scene_id, x, y)
    end
end


function action.add_skills(tb)
    local monster = data.monster
    local old = get_skills(monster)
    if 8 < #old then
        assert()
    end
    local new = {}
    for k, v in ipairs(old) do
        new[k] = v
    end
    for _, v in ipairs(tb) do
        new[#new + 1] = v
    end
    data.monster.skills = new
end

function action.change_speed(speed)
    local monster = data.monster
    if monster.base_speed == speed then
        return
    end
    monster.base_speed = speed
    monster.attr[RoleDefine.SPEED] = speed * (1 + (monster.attr[RoleDefine.SPEED_RATE] or 0))
    Obj.setSpeed(monster, monster.attr[RoleDefine.SPEED])
end

function action.change_skills(s)
    data.monster.skills = s
end


function action.cl_ai(tb_ai)
    local monster = data.monster
    local cnt = monster.ai_event_cnt
    local time = monster.ai_event_time
    if type(tb_ai) == "number" then
        cnt[tb_ai] = nil
        time[tb_ai] = nil
    else
        for _, v in ipairs(tb_ai) do
            cnt[v] = nil
            time[v] = nil
        end
    end
end

function action.cl_buf_group(group)
    Buffer.clearBuffGroup(data.monster, group, nil, true)
end


function action.del_start_point()
    local monster = data.monster
    monster.start_point_x = nil
    monster.start_point_y = nil
end
function action.destroy()
    local monster = data.monster
    Obj.resetPathParam(monster)
    monster.hp = 0
    monster.dieTime = 0
end

function action.drop_item(id, bind, cnt_min, cnt_max, step)
    for i = 1, step or 1 do
        local r = 128
        local monster = data.monster
        local x, y = monster.x + math.random(0, 2 * r) - r, monster.y + math.random(0, 2 * r) - r
        local item = ObjItem.create(id, nil, bind, math.random(cnt_min, cnt_max), x, y)
        ObjItem.enterScene(item, monster.scene_id, x, y)
    end
end

function action.du_tiao(sec)
    local monster = data.monster
    local mm = Msg.gc.GC_DU_TIAO
    mm.obj_id = monster.id
    mm.msec = sec * 1000
    Obj.sendArea(mm, monster.scene_id, monster.x, monster.y)
    monster.du_tiao_end_time = Timer.now + sec * 1000
end

function action.face(dir)
    local monster = data.monster
    if monster.status == Status.HUMAN_STATUS_GUIDE or monster.status == Status.HUMAN_STATUS_SING then
        return
    end
    if monster.dir == dir or 0 < monster.path_len then
        return
    end
    monster.dir =dir
    local mm = Msg.gc.GC_CHANGE_DIR
    mm.obj_id = monster.id
    mm.dir = dir
    Obj.sendArea(mm,monster.scene_id,monster.x,monster.y)
end

function action.face_target()
    local monster = data.monster
    local attacker = data.attacker
    if not attacker then
        return
    end
    local dir =  Obj.getDir(attacker.x-monster.x,attacker.y-monster.y)
    action.face(dir)
end

function action.fly(z, msec)
    local monster = data.monster
    monster.z = z
    local mm = Msg.gc.GC_OBJ_FLY
    mm.obj_id = monster.id
    mm.z = z
    mm.msec = msec
    Obj.sendArea(mm, monster.scene_id, monster.x, monster.y)
end

function action.go(x, y)
    on_move(data.monster, x, y)
end
function action.go_area(area_id)
    local monster = data.monster
    if not monster.scene_id then
        assert()
    end
    local map_id = Map.scene_id_to_map_id[monster.scene_id]
    if not map_id then
        assert()
    end
    if not area_id then
        assert()
    end
    if not MapEx[map_id].rect[area_id] then
        assert(nil, monster.monster_id .. " " .. map_id .. " " .. area_id)
    end
    local rect = MapEx[map_id].rect[area_id][1]
    on_move(monster, (rect[1] + rect[3]) / 2, (rect[2] + rect[4]) / 2)
end

function action.go_area_from(from_area, to_area)
    local monster = data.monster
    local map_id = Map.scene_id_to_map_id[monster.scene_id]
    --  if not Map.is_in_area_id(map_id, monster.x, monster.y, from_area) then
    --     return
    --  end
    local from = Map.getRects(map_id, from_area)
    local to   = Map.getRects(map_id, to_area)
    local srcx
    local srcy
    srcx,srcy = (from[1][1] + from[1][3])/2, (from[1][2] + from[1][4])/2
    local dstx
    local dsty
    dstx,dsty = (to[1][1] + to[1][3])/2, (to[1][2] + to[1][4])/2

    local diffx = dstx - srcx
    local diffy = dsty - srcy

    local dx = diffx + monster.x
    local dy = diffy + monster.y

    on_move(monster, dx, dy, true)
end

function action.go_start_point()
    local monster = data.monster
    local dx = monster.x - monster.start_point_x
    local dy = monster.y - monster.start_point_y
    local cf = ExcelMonster.monster[monster.monster_id]
    if 4 < (dx * dx + dy * dy) / (cf.radius_alert * cf.radius_alert) then
        action.change_speed(cf.speed_attack * 2)
    end
    on_move(monster,monster.start_point_x, monster.start_point_y)
end


function action.hp_recover()
    local monster = data.monster
    local hp_max = ObjMonster.getHpMax(monster)
    if hp_max < monster.hp + 1 then
        return
    end
    local cf = ExcelMonster.monster[monster.monster_id]
    local hp = math.floor(cf.hp_recover * cf.hp_max / 100)
    ObjMonster.setHp(monster, monster.hp + hp)
    local mm = Msg.gc.GC_OBJ_HP
    mm.obj_id = monster.id
    mm.hp = monster.hp
    mm.hp_max = hp_max
    mm.mp = 0
    mm.mp_max = 0
    Obj.sendArea(mm, monster.scene_id, monster.x, monster.y)
end

function action.jump(a, b, way)
    if not way then
        assert()
    end
    if not Msg then
        return
    end
    local attacker = data.attacker
    if not attacker then
        return
    end
    local x_old = attacker.x
    local y_old = attacker.y

    local hudu = math.random() * 2 * math.pi
    local len = math.random(a, b)
    local x = x_old + math.cos(hudu) * len
    local y = y_old + math.sin(hudu) * len
    local dx = x - x_old
    local dy = y - y_old
    local dist = dx * dx + dy * dy

    local hudu = math.random() * 2 * math.pi
    local x2 = x_old + math.cos(hudu) * len
    local y2 = y_old + math.sin(hudu) * len
    local dx = x2 - x_old
    local dy = y2 - y_old
    local monster = data.monster
    if dist < dx * dx + dy * dy then
        Obj.jump(monster, x2, y2, way == 0 and Msg.gc.GC_JUMP or way == 1 and Msg.gc.GC_CHONG_FENG or way == 2 and Msg.gc.GC_NPC_JUMP)
    else
        Obj.jump(monster, x, y,   way == 0 and Msg.gc.GC_JUMP or way == 1 and Msg.gc.GC_CHONG_FENG or way == 2 and Msg.gc.GC_NPC_JUMP)
    end
    monster.wake_up_time = Timer.now + 1000
end

function action.jump_to_owner(len)
    local monster = data.monster
    local owner = Obj.getOwner(monster)
    local hudu = math.random() * 2 * math.pi
    local x = owner.x + math.cos(hudu) * len
    local y = owner.y + math.sin(hudu) * len
    Obj.jump(monster, x, y, Msg.gc.GC_JUMP)
end

local tb_grid = {}
local function get_grid(item_id)
    local t = tb_grid[item_id]
    if t then
        return t
    end
    t = Grid.create(item_id)
    tb_grid[item_id] = t
    return t
end

local grids = {}
function action.mail(talk_id_title, talk_id_txt, item_id, cnt)
    if not cnt then
        assert()
    end
    grids[1] = get_grid(item_id)
    grids[1].bind = true
    grids[1].cnt = cnt
    local title = ExcelMonster.talk[talk_id_title].txt
    local txt = ExcelMonster.talk[talk_id_txt].txt
    local monster = data.monster
    for _, v in pairs(Obj.scenes[monster.scene_id]) do
        if v.obj_type == Obj.TYPE_HUMAN then
            local dx = v.x - monster.x
            local dy = v.y - monster.y
            if -1000 < dx and dx < 1000 and -1000 < dy and dy < 1000 then
                MailHandler.addSystemMessage(v.db._id, title, txt, grids)
            end
        end
    end
end

function action.map(map_id, area_id)
    local monster = data.monster
    Obj.leaveScene(monster)
    local rect = Map.getRects(map_id, area_id)[1]
    local x, y = (rect[1] + rect[3]) / 2, (rect[2] + rect[4]) / 2
    ObjMonster.enterScene(monster, Map.map_id_to_scene_id[map_id][1], x, y)
    local human = ObjHuman.onlineUuid[monster.marry_tour_uuid1]
    if human then
        ObjHuman.enterMap(human, map_id, x, y)
    end
    local human = ObjHuman.onlineUuid[monster.marry_tour_uuid2]
    if human then
        ObjHuman.enterMap(human, map_id, x, y)
    end
end

function action.ref_map(ai_id)
    SceneEvent.doSceneEvent(data.monster.scene_id, type(ai_id) == "number" and ai_id or ai_id[math.random(1, #ai_id)])
end

function action.ref_monster(monster_id, ai_id)
  local t = type(ai_id) == "number" and ai_id or ai_id[math.random(1, #ai_id)]
  local tb = Obj.scenes[data.monster.scene_id]
  for _, v in pairs(tb) do
    if v.monster_id == monster_id and 0 < v.hp then
        v.ref_ai_id = t
    end
  end
end

function action.ref_self(ai_id)
    data.monster.ref_ai_id = type(ai_id) == "number" and ai_id or ai_id[math.random(1, #ai_id)]
end

function action.set_nearest_human_as_owner()
    local monster = data.monster
    local x = monster.x
    local y = monster.y
    local minn = 0x7fffffff
    local ans
    for _, v in pairs(Obj.scenes[monster.scene_id]) do
        if v.obj_type == Obj.TYPE_HUMAN and 0 < v.db.hp then
            local dx = x - v.x
            local dy = y - v.y
            local t = dx * dx + dy * dy
            if t < minn then
                min = t
                ans = v
            end
        end
    end
    if ans then
        monster.owner = monster.owner or {attr_from_self = true, not_fix_owner = true}
        monster.owner.id = ans.id
        monster.owner.uid = ans.uid
    end
end

function action.set_start_point()
    local monster = data.monster
    monster.start_point_x = monster.x
    monster.start_point_y = monster.y
end

function action.speed_as_owner()
    local monster = data.monster
    local owner = Obj.getObj(monster.owner.id, monster.owner.uid)
    if not owner then
        return
    end
    local speed = owner.speed + 2
    action.change_speed(speed)
end

function action.stop()
    local monster = data.monster
    if monster.path_len < 1 then
        return
    end
    Obj.stop(monster)
end

function action.talk(talk_id)
    local mm = Msg.gc.GC_OBJ_TALK
    mm.obj_id = data.monster.id
    if not ExcelMonster.talk[talk_id] then
        assert(nil, talk_id)
    end
    mm.txt = ExcelMonster.talk[talk_id].txt
    Obj.sendArea(mm, data.monster.scene_id, data.monster.x, data.monster.y)
end

function action.transfer_self_area_id(area_id, way)
    if not Msg then
        return
    end
    if not way then
        assert()
    end
    local map_id = Map.scene_id_to_map_id[data.monster.scene_id]
    local aid = type(area_id) == "number" and area_id or area_id[math.random(1, #area_id)]
    local list_rect = Map.getRects(map_id, aid)
    local rect = list_rect[1]
    local monster = data.monster
    Obj.jump(monster, (rect[1] + rect[3]) / 2, (rect[2] + rect[4]) / 2, way == 0 and Msg.gc.GC_JUMP or way == 1 and Msg.gc.GC_CHONG_FENG or way == 2 and Msg.gc.GC_NPC_JUMP)
    monster.wake_up_time = Timer.now + 1000
end

function action.xianlv_exp()
    local monster = data.monster
    local cf = ExcelLevelReward[ExcelMarry.tour[monster.marry_tour_cf_id].level_reward]
    local human1 = ObjHuman.onlineUuid[monster.marry_tour_uuid1]
    if human1 then
        ObjHuman.addExp(human1, cf[human1.db.lv].exp)
        ObjHuman.updateLingqi(human1, cf[human1.db.lv].lingqi, "marry_tour")
    end
    local human2 = ObjHuman.onlineUuid[monster.marry_tour_uuid2]
    if human2 then
        ObjHuman.addExp(human2, cf[human2.db.lv].exp)
        ObjHuman.updateLingqi(human2, cf[human2.db.lv].lingqi, "marry_tour")
    end
    if human1 and human2 then
        FriendLogic.setQinmi(human1,human2,cf[human1.db.lv < human2.db.lv and human1.db.lv or human2.db.lv].qinmi)
    end
end

local human_list = {}
function action.xianlv_walk()
    local monster = data.monster
    human_list[1] = nil
    human_list[2] = nil
    human_list[1] = ObjHuman.onlineUuid[monster.marry_tour_uuid1]
    human_list[#human_list + 1] = ObjHuman.onlineUuid[monster.marry_tour_uuid2]
    local scene_id = monster.scene_id
    --	local path_len = monster.path_len
    --	local path = monster.path
    local x = monster.x
    local y = monster.y
    for _, human in ipairs(human_list) do
        if human.scene_id ~= scene_id then
            ObjHuman.enterMap(human, Map.scene_id_to_map_id[scene_id], x, y)
        else
            local dx = x - human.x
            local dy = y - human.y
            local r = 512
            if not(-r < dx and dx < r and -r < dy and dy < r) then
                Obj.jump(human, x, y, Msg.gc.GC_JUMP, nil, true)
            end
        end
    end
end

function action.yanhua()
    local monster = data.monster
    Obj.sendArea(Msg.gc.GC_YAN_HUA, monster.scene_id, monster.x, monster.y)
end

function action.walk_sleep()
    local monster = data.monster
    monster.walk_sleep = monster.uid
end
function action.sleep(sec)
    data.monster.wake_up_time = Timer.now + sec
end

function action.skill_owner(skill_id)
    local monster = data.monster
    local owner = Obj.getObj(monster.owner.id, monster.owner.uid)
    skill(monster, skill_id, owner.x, owner.y, owner)
end

function action.skill_area(skill_id, area_id)
    local map_id = Map.scene_id_to_map_id[data.monster.scene_id]
    local rect = MapEx[map_id].rect[area_id][1]
    skill(data.monster, skill_id, (rect[1] + rect[3]) / 2, (rect[2] + rect[4]) / 2)
end


function action.patrol()
    local monster = data.monster
    local cf = ExcelMonster.monster[monster.monster_id]
    local len = cf.radius_patrol
    if len < 1 then
        return
    end
    action.change_speed(cf.speed_alert)
    local hudu = math.random() * 2 * math.pi
    local x = monster.born_x + math.cos(hudu) * len
    local y = monster.born_y + math.sin(hudu) * len
    on_move(monster, x, y)
end

function action.rand_skill()

    local monster = data.monster
    local attacker = data.attacker
    local dx = monster.x - attacker.x
    local dy = monster.y - attacker.y
    local dist2 = dx*dx + dy*dy
    local rr = Obj.getRadius(attacker)
    local near_grid = is_near_grid(monster, attacker)
    local ans
    local minn = Timer.now * 2
    for _, skill_id in ipairs(get_skills(monster)) do
        local skill_id_first = type(skill_id) == "number" and skill_id or skill_id[1]
        local dist =  Skill.getAttackLen(skill_id_first) + rr
        if (near_grid or dist2 < dist * dist) and SkillConditions.canUseSkill(monster, skill_id_first, attacker)  == true then
            local info = CoolDown.getCoolDown(monster, skill_id_first)
            local cd = info and info.time or 10000
            if cd < minn then
                minn = cd
                ans = skill_id

            end
        end
    end
    if not ans then
        assert()
    end
    action.use_skill(ans)
end

function skill(a, skill_id, x, y, b, skillLv)
    local cf = ExcelSkill.Skill[skill_id]
    if not cf then
        assert(nil, skill_id)
    end
    if a.status == Status.HUMAN_STATUS_ROTATION or a.status == Status.HUMAN_STATUS_ROTATION1 or a.status == Status.HUMAN_STATUS_GUIDE or a.status == Status.HUMAN_STATUS_SING then
        return
    end
    if cf.xuli_sec == 0 then
        Skill.use(a, skill_id, x, y, a.x, a.y, b, skillLv)
    else
        a.qianyao_begin_time = Timer.now + cf.xuli_sec * 1000
        a.qianyao_skill_id  = skill_id
        a.qianyao_x = x
        a.qianyao_y = y
        a.qianyao_skill_lv = skillLv
        Obj.stop(a)
        a.du_tiao_end_time = Timer.now + cf.xuli_sec * 1000
        local mm = Msg.gc.GC_QIAN_YAO_DU_TIAO
        mm.obj_id = a.id
        mm.qianyao_msec = cf.xuli_sec * 1000
        mm.qianyao_effect = cf.qianyao_effect
        Obj.sendArea(mm, a.scene_id, a.x, a.y)
    end
end

function action.use_skill(skill_id)
    if not skill_id then
        assert()
    end
    --  if 1 then return end

    local monster = data.monster
    if type(skill_id) == "number" then
        if data.attacker then
            skill(monster, skill_id, data.attacker.x, data.attacker.y, data.attacker)
        else
            skill(monster, skill_id, data.monster.x, data.monster.y, data.monster)
        end
    else
        monster.use_skill_tb = skill_id
        monster.use_skill_pos = 1
    end
end



function action.speed_alert()
    local cf = ExcelMonster.monster[data.monster.monster_id]
    action.change_speed(cf.speed_alert)
end

function action.del_attacker()
    local monster = data.monster
    monster.attacker_id = nil
    monster.attacker_uid= nil
end

function action.trace(val, must)
    local monster = data.monster
    local attacker = data.attacker
    if not attacker then
        return
    end
    local xa, ya = monster.x, monster.y
    local xb, yb = attacker.x, attacker.y
    local m
    if attacker.path and attacker.path_len and 0 < attacker.path_len then
        local dx = xb - attacker.path[1]
        local dy = yb - attacker.path[2]
        if -128 < dx and dx < 128 and -128 < dy and dy < 128 then
            xb = attacker.path[1]
            yb = attacker.path[2]
            m = true
        end
    end
    if must or val == 0 and m then
        on_move(data.monster, xb, yb)
        return
    end
    
    if val == 0 then
        val = Obj.getRadius(monster) + Obj.getRadius(attacker)
        if val == 0 then
            val = 50
        end
    end

    local dx = xa - xb
    local dy = ya - yb
    local ab = math.sqrt(dx * dx + dy * dy)
    if ab < 1 then
        action.surround(val)
    else
        local x, y = xb + (xa - xb) * val / ab, yb + (ya - yb) * val / ab
        on_move(data.monster, x, y)
          on_move(data.monster, x, y)
    end
end

function action.surround(len)
    local monster = data.monster
    local attacker = data.attacker
    local hudu = math.random() * 2 * math.pi
    local x = attacker.x + math.cos(hudu) * len
    local y = attacker.y + math.sin(hudu) * len
    on_move(monster, x, y)
end

function action.surround_owner(len)
    local monster = data.monster
    local owner = Obj.getObj(monster.owner.id, monster.owner.uid)
    local hudu = math.random() * 2 * math.pi
    local x = owner.x + math.cos(hudu) * len
    local y = owner.y + math.sin(hudu) * len
    on_move(monster, x, y)
end

function action.zou_wei()
    action.change_speed(ExcelMonster.monster[data.monster.monster_id].speed_attack)
    action.trace(0, true)
end

function action.add_attacker()
    local monster = data.monster
    local attacker = data.attacker
    if SkillConditions.isEnemy(monster, attacker) and not(attacker.monster_id and get_val_by_type(attacker, "can_hit") == 0) then
        set_attacker(monster, attacker)
    end
end

function action.share_attacker_owner()
    local monster = data.monster
    local owner = Obj.getOwner(monster)
    if not owner then
        return
    end
    if owner.attacker_id then
      monster.attacker_id = owner.attacker_id
      monster.attacker_uid = owner.attacker_uid
    end
end

function action.add_attacker_in_alert(no_radius_attack)
    local monster = data.monster
    local cf = ExcelMonster.monster[monster.monster_id]
    local radius_alert = cf.radius_alert
    local radius_attack = cf.radius_attack
    local list
    local list_len
    if cf.camps == "enemy" then
        list = list_friend
        list_len = list_friend_len
    elseif cf.camps == "friend" then
        list = list_enemy
        list_len = list_enemy_len
    else
        list = list_all
        list_len = list_all_len
    end
    if 1 < list_len then
        local r = math.random(1, list_len)
        list[1], list[r] = list[r], list[1]
    end
    for i = 1, list_len do
        local v = Obj.objs[list[i]]
        if v and (v.monster_id or v.db and not is_hide(v)) and 0 < Obj.getHp(v) and SkillConditions.isEnemy(monster, v) and not(v.monster_id and get_val_by_type(v, "can_hit") == 0) then
            local dx = v.x - monster.born_x
            local dy = v.y - monster.born_y
            if no_radius_attack == 1 or dx * dx + dy * dy < radius_attack * radius_attack then
                local dx = v.x - monster.x
                local dy = v.y - monster.y
                if dx * dx + dy * dy < radius_alert * radius_alert then
                    set_attacker(monster, v)
                    break
                end
            end
        end
    end
end

function action.add_attacker_in_alert_nearest(no_radius_attack)
    local monster = data.monster
    local cf = ExcelMonster.monster[monster.monster_id]
    local radius_alert = cf.radius_alert
    local radius_attack = cf.radius_attack
    local list
    local list_len
    if cf.camps == "enemy" then
        list = list_friend
        list_len = list_friend_len
    elseif cf.camps == "friend" then
        list = list_enemy
        list_len = list_enemy_len
    else
        list = list_all
        list_len = list_all_len
    end
    if 1 < list_len then
        local r = math.random(1, list_len)
        list[1], list[r] = list[r], list[1]
    end
    local nearest_dist
    for i = 1, list_len do
        local v = Obj.objs[list[i]]
        if v and (v.monster_id or v.db and not is_hide(v)) and 0 < Obj.getHp(v) and SkillConditions.isEnemy(monster, v) and not(v.monster_id and get_val_by_type(v, "can_hit") == 0) then
            local dx = v.x - monster.born_x
            local dy = v.y - monster.born_y
            if no_radius_attack == 1 or dx * dx + dy * dy < radius_attack * radius_attack then
                local dx = v.x - monster.x
                local dy = v.y - monster.y
                local dd =  dx * dx + dy * dy 
                if dd < radius_alert * radius_alert and (not nearest_dist or dd < nearest_dist ) then
                    set_attacker(monster, v)
                    nearest_dist = dd
                end
            end
        end
    end
end
function action.home()
    local monster = data.monster
    local dx = monster.x - monster.born_x
    local dy = monster.y - monster.born_y
    local cf = ExcelMonster.monster[monster.monster_id]
    local mul = (dx * dx + dy * dy) / (cf.radius_alert * cf.radius_alert)
    if 4 < mul then
        action.change_speed(cf.speed_attack * 2)
    end
    on_move(monster,monster.born_x, monster.born_y)
end

function action.set(k, str)
    local monster = data.monster
    local fun
    if action_set[str] then
        fun = action_set[str]
    else
        local t, err = load("return function(a)return " .. str .. " end")
        if not t then
            assert(nil, err)
        end
        fun = t()
        action_set[str] = fun
    end
    local t = get_val(monster)
    t[k] = t[k] or 0
    t[k] = fun(t)
end



-- 条件实现
function condition.can_add_attacker_in_alert(no_radius_attack)
    local monster = data.monster
    local cf = ExcelMonster.monster[monster.monster_id]
    local radius_alert = cf.radius_alert
    local radius_attack = cf.radius_attack
    local list
    local list_len
    if cf.camps == "enemy" then
        list = list_friend
        list_len = list_friend_len
    elseif cf.camps == "friend" then
        list = list_enemy
        list_len = list_enemy_len
    else
        list = list_all
        list_len = list_all_len
    end
    for i = 1, list_len do
        local v = Obj.objs[list[i]]
        if v and (v.monster_id or v.db and not is_hide(v)) and 0 < Obj.getHp(v) and SkillConditions.isEnemy(monster, v) then
            local dx = v.x - monster.born_x
            local dy = v.y - monster.born_y
            if no_radius_attack == 1 or dx * dx + dy * dy < radius_attack * radius_attack then
                local dx = v.x - monster.x
                local dy = v.y - monster.y
                if dx * dx + dy * dy < radius_alert * radius_alert and v.id ~= monster.attacker_id then
                    return true
                end
            end
        end
    end
end

function condition.can_use_skill()
    local monster = data.monster
    local attacker = data.attacker
    local dx = monster.x - attacker.x
    local dy = monster.y - attacker.y
    local dist2 = dx*dx + dy*dy
    local rr = Obj.getRadius(attacker)
    local near_grid = is_near_grid(monster, attacker)
    for _, skill_id in ipairs(get_skills(monster)) do
        if SkillConditions.canUseSkill(monster, skill_id, attacker)  == true then
            if near_grid then
                return true
            end
            local dist = Skill.getAttackLen(skill_id) + rr
            if dist2 < dist * dist then
                return true
            end
        end
    end
end

function condition.can_use_skill_id(skill_id)
    local monster = data.monster
    local attacker = data.attacker
    local dx = monster.x - attacker.x
    local dy = monster.y - attacker.y
    local dist2 = dx * dx + dy * dy
    local dist = Skill.getAttackLen(skill_id)  + Obj.getRadius(attacker)
    if dist2 < dist * dist and SkillConditions.canUseSkill(monster, skill_id, attacker)  == true then
        return true
    end
end

function condition.can_zou_wei()
    local monster = data.monster
    if #get_skills(monster) < 1 then
        return
    end
    local attacker = data.attacker
    local dx = monster.x - attacker.x
    local dy = monster.y - attacker.y
    local dist2 = dx*dx + dy*dy
    local minn = Timer.now * 3
    local rr = Obj.getRadius(attacker)
    local ans
    for _, skill_id in ipairs(get_skills(monster)) do
        local tmp = CoolDown.getSkillNextCanUseTime(monster, skill_id)
        if tmp < minn then
            ans = Skill.getAttackLen(skill_id) + rr
            minn = tmp
        end
    end
    if not(0 < monster.path_len and attacker.x == monster.tx and monster and attacker.y == monster.ty) then
        if ans == 100 then
            return not is_near_grid(monster, attacker)
        else
            return ans * ans < dist2
        end
    end
end

function condition.check(str)
    local monster = data.monster
    local fun
    if condition_check[str] then
        fun = condition_check[str]
    else
        fun = load("return function(a)return " .. str .. " end")()
        condition_check[str] = fun
    end
    return fun(get_val(monster))
end

function condition.create_time(a, b)
    if not b or b <= a then
        b = 0x7fffffff
    end
    local d = Timer.now - data.monster.uid
    return 1000 * a <= d and d <= 1000 * b
end

function condition.can_move()
    return Status.canMove(data.monster)
end

function condition.dist(a, b)
    local dx = data.monster.x - data.attacker.x
    local dy = data.monster.y - data.attacker.y
    local sum = dx * dx + dy * dy
    return a * a <= sum and sum <= b * b
end

function condition.finish_recur(ai_id, sec)
    local monster = data.monster
    local cur_ai = data.cur_ai
    return monster.ai_event_cnt[ai_id] and (not monster.ai_event_cnt[cur_ai] or monster.ai_event_cnt[cur_ai] < monster.ai_event_cnt[ai_id]) and sec * 1000 < Timer.now - monster.ai_event_time[ai_id]
end

function condition.finish_simple(ai_id, sec)
    local monster = data.monster
    return monster.ai_event_cnt[ai_id] and ExcelMonster.ai[ai_id].cnt <= monster.ai_event_cnt[ai_id] and sec * 1000 <= Timer.now - monster.ai_event_time[ai_id]
end

function condition.has_target()
    return data.monster.attacker_id
end

function condition.has_lock_target()
    local monster = data.monster
    return monster.lock_target == monster.uid
end

function condition.has_owner()
    local monster = data.monster
    local owner = Obj.getOwner(monster)
    if not owner then
        return
    end
    if Obj.getHp(owner) < 1 then
        return
    end
    if owner.scene_id ~= monster.scene_id then
        return
    end
    return true
end

function condition.has_start_point()
    return data.monster.start_point_x
end

function condition.hp(a, b)
    local monster = data.monster
    local d = monster.hp / monster.attr[RoleDefine.HP_MAX] * 100
    return a <= d and d <= b
end

function condition.is_attacker_stop()
    local t = data.attacker.path_len
    return not t or t < 1
end

function condition.is_face(dir)
    return data.monster.dir == dir
end

function condition.is_face_target()
    local monster = data.monster
    local attacker = data.attacker
    return monster.dir ==  Obj.getDir(attacker.x-monster.x,attacker.y-monster.y)
end

function condition.is_going_area(area_id)
    local monster = data.monster
    if monster.path_len < 1 or not monster.path[1] then
        return
    end
    return is_in_area_id(monster.scene_id, monster.path[1], monster.path[2], area_id)
end

function condition.is_stop()
    return data.monster.path_len<1
end

function condition.is_giddy()
    return BufferCmds.passive.giddy(data.monster)
end

function condition.map_has_human(cnt)
    local sum = 0
    local tb = Obj.scenes[data.monster.scene_id]
    for kkk = 1, tb[0] do
        local v = tb[kkk]
        if v.obj_type == Obj.TYPE_HUMAN then
            sum = sum + 1
            if cnt <= sum then
                return true
            end
        end
    end
end

function condition.over_lock_target_time(val)
    return val * 1000 < Timer.now - data.monster.lock_target_time
end

function condition.over_owner_dist(len)
    local monster = data.monster
    local owner = Obj.getObj(monster.owner.id, monster.owner.uid)
    local dx = owner.x - monster.x
    local dy = owner.y - monster.y
    return len * len < dx * dx + dy * dy
end

function condition.over_radius_patrol()
    local monster = data.monster
    local dx = monster.x - monster.born_x
    local dy = monster.y - monster.born_y
    local d = ExcelMonster.monster[monster.monster_id].radius_patrol + 70
    return d * d < dx * dx + dy * dy
end

function condition.over_radius_attack()
    local monster = data.monster
    local sx = monster.start_point_x or monster.born_x
    local sy = monster.start_point_y or monster.born_y
    local dx = monster.x - sx
    local dy = monster.y - sy
    local d = ExcelMonster.monster[monster.monster_id].radius_attack
    if d * d < dx * dx + dy * dy then
        return true
    end
    local attacker = data.attacker
    if attacker then
        dx = attacker.x - sx
        dy = attacker.y - sy
        return d * d < dx * dx + dy * dy
    end
end

function condition.reach(x, y)
    local dx = data.monster.x - x
    local dy = data.monster.y - y
    local r = 51
    return -r < dx and dx < r and -r < dy and dy < r
end

function condition.reach_area(area_id)
    local monster = data.monster
    return is_in_area_id(monster.scene_id, monster.x, monster.y, area_id)
end

function condition.same_pos()
    local monster = data.monster
    local attacker = data.attacker
    if not attacker.path_len then
        return
    end
    if 0 < monster.path_len or 0 < attacker.path_len then
        return
    end

    local dx = monster.x - attacker.x
    local dy = monster.y - attacker.y
    local r = Obj.getRadius(monster) + Obj.getRadius(attacker) - 3
    if dx * dx + dy * dy < r * r and 100 < r then
        return true
    end
    if math.abs(dx) < 90 and math.abs(dy) < 90 then
        return true
    end
    local tile = 100
    return math.floor(monster.x / tile) == math.floor(attacker.x / tile) and math.floor(monster.y / tile) == math.floor(attacker.y / tile)
end

function condition.same_pos_with_monster()
    local monster = data.monster
    if not monster.path_len or monster.path_len > 0 then
        return
    end
    if true then
        return
    end
    return monster.same_pos_monster
end

function condition.see_human()
    local monster = data.monster
    return lua_aoi.grid9_get(monster.x, monster.y)
end

local tb = {}
for k, v in pairs(condition) do
    tb["not_" .. k] = function(a, b) return not v(a, b) end
end
for k, v in pairs(tb) do
    condition[k] = v
end
tb = nil
